package mosdi.util;

import java.util.Iterator;
import java.util.NoSuchElementException;

/** Class that collects values and produces a histogram from these values. */
public class Histogram implements Iterable<Histogram.Bin> {
	
	private double minValue;
	private double maxValue;
	private int binCount;
	private double binWidth;

	private long[] counts;
	
	public Histogram(double minValue, double maxValue, int binCount) {
		this.minValue = minValue;
		this.maxValue = maxValue;
		this.binCount = binCount;
		this.binWidth = (maxValue-minValue)/binCount;
		counts = new long[binCount];
	}
	
	/** Adds a value to be counted. */
	public void add(double value) {
		try {
			int k = (int)((value-minValue)/binWidth);
			// take care of border cases: map maxValue into last bin
			if ((value<=maxValue) && (k==binCount)) k=binCount-1;
			counts[k] += 1;
		} catch (IndexOutOfBoundsException e) {
			throw new IllegalArgumentException("Value out of range: "+value);
		}
	}
	
	private class BinIterator implements Iterator<Bin> {
		private int next;
		private BinIterator() {
			this.next = 0;
		}
		@Override
		public boolean hasNext() {
			return next<counts.length;
		}
		@Override
		public Bin next() {
			if (next>=counts.length) throw new NoSuchElementException();
			return new Bin(next++);
		}
		@Override
		public void remove() {
			throw new UnsupportedOperationException();
		}
	}
	
	public class Bin {
		private int binIndex;
		private Bin(int binIndex) {
			this.binIndex = binIndex;
		}
		public long getCount() {
			return counts[binIndex];
		}
		public double getIntervalStart() {
			return binWidth*binIndex + minValue;
		}
		public double getIntervalMiddle() {
			return binWidth*(0.5+binIndex) + minValue;
		}
		public double getIntervalEnd() {
			return binWidth*(1.0+binIndex) + minValue;
		}
	}

	@Override
	public Iterator<Bin> iterator() {
		return new BinIterator();
	}
	
}
